﻿using System;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using SoftwareConsulting.BI11.Studio.BasicEntities;
using SoftwareConsulting.BI11.Studio.Core.Project;
using SoftwareConsulting.BI11.Studio.Core.Project.SituationsHierarchy;
using SoftwareConsulting.BI11.Studio.InternalServices;
using SoftwareConsulting.BI11.Studio.ProcessWait;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.NumericalMethods.Geometry;

namespace SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.SituationsHierarchy
{
    /// <summary>
    /// Класс рабочего пространства иерархии ситуаций
    /// </summary>
    [Serializable]
    public sealed class SituationsArea : Hierarchy<SituationBox>
    {
        #region константы
        [NonSerialized]public const int MouseSituationDistance = 30;
        [NonSerialized]public const int SituationsAreaRefreshInterval = 50; //оптимальный минимальный интервал между перерисовками РП        
        [NonSerialized]public const int MapRefreshInterval = 200; //оптимальный минимальный интервал между перерисовками карты
        [NonSerialized]public const int MapMaxRefreshTime = 180; // максимальное время на перерисовку карты
        [NonSerialized]public const int RefreshIntervalMaxOffset = 20; // максимальное незначимое отклонение предлагаемого интервала обновлений от текущего интервала (если будет больше, то считается значимым, и, соответственно, предлагаемый интервал становится текущим)                        
        [NonSerialized]public const int SituationsAreaMaxRefreshTime = 50; //максимальное время на перерисовку РП        
        [NonSerialized]public const int ScrollByMouseDown = 30;        
        #endregion        

        #region поля
        [NonSerialized]private IHierarchy<LinkLine> _linkLines; //список линий связей между ситуациями
        [NonSerialized]private SituationBox _selectedSituationBox; //сфокусированный в данный момент прямоугольник ситуации
        [NonSerialized]private LinkLine _selectedLinkLine; //сфокусированная в данный момент линия связи
        [NonSerialized]private SituationBox _hoveredNonselectedSituationBox; //подсвеченный невыделенный прямоугольник ситуации
        [NonSerialized]private SituationBox _savingScaleSituationBox; //прямоугольник ситуаци, на котором определяются коэффициенты масштабирования при сериализации
        [NonSerialized]private SituationsHierarchyState _sh_state; //текущее состояние редактирования РП                
        [NonSerialized]private Directions2D _movestate; //состояние продвижения по РП        
        [NonSerialized]private IHierarchy<SituationBox> _multiselectedSituationBoxes; //множественно выделенные прямоугольники ситуаций
        [NonSerialized]private SituationsHierarchyScale _scale; //масштаб
        [NonSerialized]private bool _scaling; //флаг изменения масштаба        
        [NonSerialized]private bool _blockRectangleScaleRecalculating; //флаг запрета перерасчете коэффициента масштабирования при изменении SCALE
        [NonSerialized]private float _scalingFactorForRectangleX; //коэффициент масштабирования для прямоугольников по X
        [NonSerialized]private float _scalingFactorForRectangleY; //коэффициент масштабирования для прямоугольников по Y        
        [NonSerialized]private bool _IsLinkCreatingProcess; //флаг установки связи
        [NonSerialized]private bool _IsMultiselect; //флаг множественного выделения на РП        
        [NonSerialized]private bool _IsMultiselectByCtrl; //флаг множественного выделения на РП по клавише Ctrl
        [NonSerialized]private Point _MultiselectionStartPoint; //стартовая точка множественного выделения на РП
        [NonSerialized]private Point _MultiselectionEndPoint; //конечная точка множественного выделения на РП                                
        [NonSerialized]private SituationsHierarchyForm _situationsHierarchyForm; //для связи с окном иерахии ситуаций
        [NonSerialized]private ProjectDescriptor _projectLink; //для связи с проектом
        [NonSerialized]private ProjectExplorerForm _projectExplorerForm;        
        #endregion

        #region публичные свойства   
        /// <summary>
        /// Возвращает/устанавливает ссылку на окно иерархии ситуаций
        /// </summary>
        public SituationsHierarchyForm SituationsHierarchyForm
        {
            get
            {
                return _situationsHierarchyForm;
            }
            set
            {
                _situationsHierarchyForm = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает состояние продвижения по области ситуаций
        /// </summary>        
        public Directions2D MovementWay
        {
            get
            {
                return _movestate;
            }
            set
            {
                _movestate = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг запрета перерасчете коэффициента масштабирования при изменении масштаба
        /// </summary>        
        public bool BlockRectangleScaleRecalculating
        {
            get
            {
                return _blockRectangleScaleRecalculating;
            }
            set
            {
                _blockRectangleScaleRecalculating = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает прямоугольник ситуации, на котором определяются коэффициенты масштабирования при сериализации
        /// </summary>        
        public SituationBox SavingScaleSituationBox
        {
            get
            {
                return _savingScaleSituationBox;
            }
            set
            {
                _savingScaleSituationBox = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает коэффициент масштабирования прямоугольника ситуации по X
        /// </summary>        
        public float ScalingFactorForRectangleX
        {
            get
            {
                return _scalingFactorForRectangleX;
            }
            set
            {
                _scalingFactorForRectangleX = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает коэффициент масштабирования прямоугольника ситуации по Y
        /// </summary>        
        public float ScalingFactorForRectangleY
        {
            get
            {
                return _scalingFactorForRectangleY;
            }
            set
            {
                _scalingFactorForRectangleY = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает подсвеченный невыбранный прямоугольник ситуации
        /// </summary>        
        public SituationBox HoveredNonselectedSituationBox
        {
            get
            {
                return _hoveredNonselectedSituationBox;
            }
            set
            {
                _hoveredNonselectedSituationBox = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает связь с проектом
        /// </summary>        
        public ProjectDescriptor ProjectLink
        {
            get
            {
                return _projectLink;
            }
            set
            {
                _projectLink = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает связь с формой обозревателя проекта
        /// </summary>        
        public ProjectExplorerForm ProjectExplorerLink
        {
            get
            {
                return _projectExplorerForm;
            }
            set
            {
                _projectExplorerForm = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает список линий связи области ситуаций
        /// </summary>        
        public IHierarchy<LinkLine> LinkLines
        {
            get
            {
                return _linkLines;
            }
            set
            {
                _linkLines = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает список множественно выделенных прямоугольников ситуаций
        /// </summary>        
        public IHierarchy<SituationBox> MultiselectedSituationBoxes
        {
            get
            {
                return _multiselectedSituationBoxes;
            }
            set
            {
                _multiselectedSituationBoxes = value;
                if ((value != null) && (value.ItemsCount > 0))
                {
                    _selectedSituationBox = null;
                    _selectedLinkLine = null;
                }
                _situationsHierarchyForm.RefreshSelectedSituationActionsAbilities();
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг установки связи
        /// </summary>        
        public bool IsLinkCreatingProcess
        {
            get
            {
                return _IsLinkCreatingProcess;
            }
            set
            {
                _IsLinkCreatingProcess = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг множестенного выделения в области ситуаций по клавише Ctrl
        /// </summary>        
        public bool IsMultiselectByCtrl
        {
            get
            {
                return _IsMultiselectByCtrl;
            }
            set
            {
                _IsMultiselectByCtrl = value;
            }
        }

        /// <summary>
        /// Возвращает размеры области ситуаций
        /// </summary>
        /// <returns></returns>
        public Size SituationsAreaSize
        {
            get
            {
                return _situationsHierarchyForm.SituationsAreaSize;
            }
        }


        /// <summary>
        /// Возвращает/устанавливает флаг множестенного выделения в области ситуаций
        /// </summary>        
        public bool IsMultiselect
        {
            get
            {
                return _IsMultiselect;
            }
            set
            {
                _IsMultiselect = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает конечную точку множестенного выделения в области ситуаций
        /// </summary>        
        public Point MultiselectionEndPoint
        {
            get
            {
                return _MultiselectionEndPoint;
            }
            set
            {
                _MultiselectionEndPoint = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает стартовую точку множестенного выделения в области ситуаций
        /// </summary>        
        public Point MultiselectionStartPoint
        {
            get
            {
                return _MultiselectionStartPoint;
            }
            set
            {
                _MultiselectionStartPoint = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг изменения масштаба
        /// </summary>        
        public bool Scaling
        {
            get
            {
                return _scaling;
            }
            set
            {
                _scaling = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает масштаб
        /// </summary>               
        public SituationsHierarchyScale Scale
        {
            get
            {
                return _scale;
            }
            set
            {                
                _scale = value;
                _situationsHierarchyForm.SetSituationsAreaPanelLayout(false);                
                int c = ItemsCount;
                int i = 0;                
                if (!_blockRectangleScaleRecalculating)
                {
                    _scalingFactorForRectangleX = -1;
                    _scalingFactorForRectangleY = -1;
                }
                _blockRectangleScaleRecalculating = false;
                foreach (SituationBox sitbox in this)
                {
                    _scaling = i != (c - 1);                    
                    sitbox.ApplyScale(Scale);
                    i++;
                }
                if (!ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Serialization))
                {                    
                    if (_selectedSituationBox != null)
                        _situationsHierarchyForm.ScrollToSituationBox(_selectedSituationBox);                    
                    if ((_multiselectedSituationBoxes != null) && (_multiselectedSituationBoxes.ItemsCount > 0))
                    {
                        foreach (SituationBox sitBox in _multiselectedSituationBoxes)
                        {
                            _situationsHierarchyForm.ScrollToSituationBox(sitBox);
                            break;
                        }
                    }
                }
                _situationsHierarchyForm.SetSituationsAreaPanelLayout(true);
            }
        }

        /// <summary>
        /// Возвращает/устанавливает выделенный прямоугольник ситуации
        /// </summary>        
        public SituationBox SelectedSituationBox
        {
            get
            {
                return _selectedSituationBox;
            }
            set
            {
                SituationBox before = _selectedSituationBox;
                _selectedSituationBox = value;                
                if (before != null)
                    before.UpdateView();                
                if (_selectedSituationBox != null)
                {
                    _selectedLinkLine = null;
                    _selectedSituationBox.UpdateView();
                }
                _situationsHierarchyForm.RefreshSelectedSituationActionsAbilities();
            }
        }

        /// <summary>
        /// Возвращает/устанавливает выделенную линию связи
        /// </summary>        
        public LinkLine SelectedLinkLine
        {
            get
            {
                return _selectedLinkLine;
            }
            set
            {
                _selectedLinkLine = value;
                if (value != null)
                {
                    _selectedSituationBox = null;
                    MultiselectedSituationBoxes = null;
                }
            }
        }

        /// <summary>
        /// Возвращает/устанавливает состояние редактирования иерархии
        /// </summary>        
        public SituationsHierarchyState State
        {
            get
            {
                return _sh_state;
            }
            set
            {
                _sh_state = value;
                bool oldLinkCreatingProcess = _IsLinkCreatingProcess;
                _IsLinkCreatingProcess = false;
                if (_projectLink != null)
                {
                    SituationBox sitbox = GetStartLinkPoint();
                    if (sitbox != null)
                        sitbox.IsLinkStart = false;                    
                    if (oldLinkCreatingProcess)
                        _situationsHierarchyForm.RefreshSituationsArea();                    
                }                
            }
        }
        #endregion

        #region внутренние методы
        /// <summary>
        /// Выбирает следующий прямоугольник ситуации из этой же линии для перехода по табуляции
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="situationBoxesOfSameRow"></param>
        /// <param name="notStrongCompare"></param>
        /// <param name="strongCompare"></param>
        /// <returns></returns>
        private SituationBox GetNextSituationBoxInSameRow(TaborderDirection direction, IHierarchy<SituationBox> situationBoxesOfSameRow, GeneralMethods.CompareOperation notStrongCompare, GeneralMethods.CompareOperation strongCompare)
        {
            SituationBox choosedSitBox = null;
            int extremumx = direction == TaborderDirection.Forward ? int.MaxValue : int.MinValue;
            foreach (SituationBox sitBoxOfSameRow in situationBoxesOfSameRow)
            {
                if (!notStrongCompare(sitBoxOfSameRow.X, extremumx))
                {
                    if (sitBoxOfSameRow.X == extremumx)
                    {
                        //ситуации лежат на одной точке
                        //значит вперед берем ситуацию с меньшим идентификатором, назад - с большим
                        if ((choosedSitBox != null) && (strongCompare(sitBoxOfSameRow.SituationId.CompareTo(choosedSitBox.SituationId), 0)))
                            continue;
                    }
                    choosedSitBox = sitBoxOfSameRow;
                    extremumx = sitBoxOfSameRow.X;
                }
            }
            return choosedSitBox;
        }

        /// <summary>
        /// Инициализирует класс
        /// </summary>        
        private void InitializeClass()
        {
            _linkLines = new Hierarchy<LinkLine>();
            _sh_state = SituationsHierarchyState.Pointer;
            _scale = SituationsHierarchyScale.Normal;
            _scaling = false;
            _IsLinkCreatingProcess = false;
            _IsMultiselect = false;
            _IsMultiselectByCtrl = false;
            _hoveredNonselectedSituationBox = null;
            _blockRectangleScaleRecalculating = false;
            _movestate = Directions2D.None;            
        }

        [OnDeserializedAttribute()]
        private void WhenDeserialized(StreamingContext context)
        {
            InitializeClass();
        }

        /// <summary>
        /// Восстанавливает обратные связи после десериализации
        /// </summary>
        private void RestoreBackLinks(SituationsArea restoringSituationsArea, int minPercent, int maxPercent)
        {
            Processing.SetPercentOfPartByBorders(0, minPercent, maxPercent);
            restoringSituationsArea.ProjectLink = _projectLink;
            restoringSituationsArea.ProjectExplorerLink = _projectExplorerForm;
            restoringSituationsArea.SituationsHierarchyForm = _situationsHierarchyForm;
            int c = restoringSituationsArea.ItemsCount;
            int i = 0;
            foreach(SituationBox sitbox in restoringSituationsArea)
            {                
                sitbox.Parent = restoringSituationsArea;
                sitbox.ProjectLink = restoringSituationsArea.ProjectLink;
                sitbox.ProjectExplorerLink = restoringSituationsArea.ProjectExplorerLink;
                i++;
                Processing.SetPercentOfPartByBorders(Convert.ToInt32(i / c * 100), minPercent, maxPercent);
            }
            Processing.SetPercentOfPartByBorders(100, minPercent, maxPercent);
        }

        /// <summary>
        /// Восстанавливает текстбоксы ситуаций
        /// </summary>
        private void RestoreSituationBoxes(SituationsArea restoringSituationsArea, int minPercent, int maxPercent)
        {
            Processing.SetPercentOfPartByBorders(0, minPercent, maxPercent);
            ProjectDescriptor curProject = _projectLink == null ? _situationsHierarchyForm.ProjectLink : _projectLink;            
            int c = restoringSituationsArea.ItemsCount;
            int i = 0;
            foreach(SituationBox sitbox in restoringSituationsArea)
            {                
                sitbox.CreateRectangle(true);
                _situationsHierarchyForm.UpdateTextBoxParent(sitbox);
                i++;
                Processing.SetPercentOfPartByBorders(Convert.ToInt32(i / c * 100), minPercent, maxPercent);
            }
            Processing.SetPercentOfPartByBorders(100, minPercent, maxPercent);
        }     
        #endregion
        
        #region публичные методы        
        /// <summary>
        /// Создает новый прямоугольник ситуации
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="situation"></param>
        /// <returns></returns>
        public SituationBox CreateSituationBox(int x, int y, Situation situation)
        {
            SituationBox sitbox = new SituationBox(x, y, situation, this);
            sitbox.UpdateView();                        
            Add(sitbox);
            return sitbox;    
        }

        /// <summary>
        /// Программный выбор ситуации
        /// </summary>
        /// <param name="situation"></param>
        public void GotoSituationBoxBySituation(Situation situation)
        {
            SelectedSituationBox = GetSituationBoxBySituation(situation);
            TextBox tb = SelectedSituationBox.Control;
            bool toCenter = !_situationsHierarchyForm.IsRectangleFullVisible(GeometricMethods.TwoPointsToRectangle(tb.Location, new Point(tb.Left + tb.Width, tb.Top + tb.Height)));
            tb.Focus();
            _situationsHierarchyForm.UpdateCharacteristicsGrid(situation);
            if (toCenter)
                _situationsHierarchyForm.CenterSelectedSituation();            
        }

        /// <summary>
        /// Программный выбор связи
        /// </summary>
        /// <param name="link"></param>
        public void GotoLinkLineByLink(Link link)
        {
            LinkLine ll = GetLinkLineByLink(link);            
            SelectedLinkLine = ll;
            IHierarchy<SituationBox> sbs = new Hierarchy<SituationBox>();
            ActualizeScheme(sbs, true);
            _situationsHierarchyForm.FocusSituationsArea();
            _situationsHierarchyForm.UpdateCharacteristicsGrid(null);
            _situationsHierarchyForm.CenterSituationBox(GetSituationBoxBySituation(_projectLink.GetCharacteristicById(link.SourceCharacteristicId).OwningSituation));
        }

        /// <summary>
        /// Открытие области ситуаций по указанному пути
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public SituationsArea Open(string path, int minPercent, int maxPercent)
        {
            return DeserializeMe(path, minPercent, maxPercent);
        }

        /// <summary>
        /// Возвращает расширение имени файла иерархии ситуаций
        /// </summary>
        public static string FileNameExtension
        {
            get
            {
                return Resources.BI_PROJECT_SITUATIONSHIERARCHY_EXT_REAL;
            }
        }

        /// <summary>
        /// Сохранение текущей области ситуаций
        /// </summary>
        public void Save()
        {
            ProjectDescriptor curProject = _projectLink;
            if (curProject == null)
            {
                curProject = _situationsHierarchyForm.ProjectLink;
            }
            SerializeMe(curProject.Path + @"\" + curProject.Name + FileNameExtension);
        }        

        /// <summary>
        /// Возвращает следующий прямоугольник ситуации для табуляции
        /// </summary>
        /// <returns></returns>
        public SituationBox GetNextSituationBoxByTabOrder(TaborderDirection direction)
        {
            int Y = direction == TaborderDirection.Forward ? -1 : int.MaxValue;
            int X = Y;
            if (_selectedSituationBox != null)
            {
                X = _selectedSituationBox.X;
                Y = _selectedSituationBox.Y;
            }            
            GeneralMethods.CompareOperation strongCompare = null;
            GeneralMethods.CompareOperation notStrongCompare = null;            
            switch (direction)
            {
                case TaborderDirection.Forward:
                    strongCompare = GeneralMethods.IsMore;
                    notStrongCompare = GeneralMethods.IsMoreOrEquals;                    
                    break;
                case TaborderDirection.Backward:
                    strongCompare = GeneralMethods.IsLess;
                    notStrongCompare = GeneralMethods.IsLessOrEquals;                    
                    break;
            }
            int mindify = int.MaxValue;            
            IHierarchy<SituationBox> situationBoxesOfSameRow = new Hierarchy<SituationBox>();
            foreach(SituationBox anySitBox in this)
            {                
                if (anySitBox != _selectedSituationBox)
                {
                    if (strongCompare(anySitBox.Y, Y) || (anySitBox.Y == Y) && notStrongCompare(anySitBox.X, X))
                    {                        
                        //*************************
                        //для исключения ситуаций, лежащих там же, где текущая, которые уже были пройдены
                        if ((anySitBox.X == X) && (anySitBox.Y == Y))
                        {
                            if (!strongCompare(anySitBox.SituationId.CompareTo(_selectedSituationBox.SituationId), 0))
                            { //вперед идем от ситуаций с меньшим идентификатором к ситуациям с большим
                              //назад - наоборот
                                continue;
                            }
                        }
                        //*************************                        
                        int dif = (anySitBox.Y - Y);
                        dif = direction == TaborderDirection.Forward ? dif : dif * -1;
                        if (dif < mindify)
                        {
                            situationBoxesOfSameRow.Clear();
                            mindify = dif;                            
                        }
                        if (dif <= mindify)
                        {
                            situationBoxesOfSameRow.Add(anySitBox);
                        }                        
                    }
                }
            }
            SituationBox choosedSitBox = GetNextSituationBoxInSameRow(direction, situationBoxesOfSameRow, notStrongCompare, strongCompare);            
            if (choosedSitBox == null)
            {
                //так и не нашли нужный прямоугольник
                //значит вперед берем самый верхний левый,
                //назад - самый нижний правый
                int extremumy = direction == TaborderDirection.Forward ? int.MaxValue : int.MinValue;
                situationBoxesOfSameRow = new Hierarchy<SituationBox>();
                foreach(SituationBox anySitBox in this)
                {                    
                    if (anySitBox != SelectedSituationBox)
                    {
                        if (!notStrongCompare(anySitBox.Y, extremumy))                        
                        {
                            if (!strongCompare(anySitBox.Y, extremumy))                            
                            {
                                situationBoxesOfSameRow.Clear();
                                extremumy = anySitBox.Y;
                            }
                            if (!notStrongCompare(anySitBox.Y, extremumy))
                            {
                                situationBoxesOfSameRow.Add(anySitBox);
                            }
                        }
                    }
                }
                choosedSitBox = GetNextSituationBoxInSameRow(direction, situationBoxesOfSameRow, notStrongCompare, strongCompare);                
            }
            return choosedSitBox;
        }        
        /*
        /// <summary>
        /// Возвращает предыдущий прямоугольник ситуации для табуляции
        /// </summary>
        /// <returns></returns>
        public SituationBox GetPreviousSituationBoxByTabOrder()
        {
            int Y = int.MaxValue;
            int X = int.MaxValue;
            if (_selectedSituationBox != null)
            {
                X = _selectedSituationBox.X;
                Y = _selectedSituationBox.Y;
            }                        
            int miny = int.MaxValue;
            Hashtable linesituationboxes = new Hashtable();
            foreach(SituationBox sitbox2 in this)
            {                
                if (sitbox2 != SelectedSituationBox)
                {
                    if ((sitbox2.Y < Y) || (sitbox2.Y == Y) && (sitbox2.X <= X))
                    {
                        bool toContinue = true;
                        //*************************
                        //для исключения ситуаций, лежащих там же, где текущая, которые уже были пройдены
                        if ((sitbox2.X == X) && (sitbox2.Y == Y))
                        {
                            if (sitbox2.RelatedSituationId.CompareTo(_selectedSituationBox.RelatedSituationId) > 0)
                            { //назад идем от ситуаций с большим идентификатором к ситуациям с меньшим
                                toContinue = false;
                            }
                        }
                        //*************************
                        if (toContinue)
                        {
                            int dif = (Y - sitbox2.Y);
                            if (dif < miny)
                            {
                                linesituationboxes.Clear();
                                miny = Y - sitbox2.Y;
                            }
                            if (dif <= miny)
                            {
                                linesituationboxes.Add(sitbox2.Id, sitbox2);
                            }
                        }
                    }
                }
            }
            SituationBox sitbox = null;            
            int maxx = int.MinValue;
            foreach(DictionaryEntry entry in linesituationboxes)
            {
                SituationBox sitbox2 = entry.Value as SituationBox;
                if (sitbox2.X >= maxx)
                {
                    bool toContinue = true;
                    if (sitbox2.X == maxx)
                    {
                        //ситуации лежат на одной точке
                        //значит берем ситуацию с большим идентификатором
                        if ((sitbox != null) && (sitbox2.RelatedSituationId.CompareTo(sitbox.RelatedSituationId) < 0))
                        {
                            toContinue = false;
                        }
                    }
                    if (toContinue)
                    {
                        sitbox = sitbox2;
                        maxx = sitbox2.X;
                    }
                }
            }
            if (sitbox == null)
            {
                //так и не нашли нужный прямоугольник
                //значит берем самый нижний правый                
                int maxy = int.MinValue;
                linesituationboxes = new Hashtable();
                foreach(SituationBox sitbox2 in this)
                {                    
                    if (sitbox2 != SelectedSituationBox)
                    {
                        if (sitbox2.Y >= maxy)
                        {
                            if (sitbox2.Y > maxy)
                            {
                                linesituationboxes.Clear();
                                maxy = sitbox2.Y;
                            }
                            if (sitbox2.Y >= maxy)
                            {
                                linesituationboxes.Add(sitbox2.Id, sitbox2);
                            }
                        }
                    }
                }                
                maxx = int.MinValue;
                foreach(DictionaryEntry entry in linesituationboxes)
                {
                    SituationBox frbox2 = entry.Value as SituationBox;
                    if (frbox2.X >= maxx)
                    {
                        bool toContinue = true;
                        if (frbox2.X == maxx)
                        {
                            //ситуации лежат на одной точке
                            //значит берем ситуацию с большим идентификатором
                            if ((sitbox != null) && (frbox2.RelatedSituationId.CompareTo(sitbox.RelatedSituationId) < 0))
                            {
                                toContinue = false;
                            }
                        }
                        if (toContinue)
                        {
                            sitbox = frbox2;
                            maxx = frbox2.X;
                        }
                    }
                }
            }
            return sitbox;
        }*/

        /// <summary>
        /// Возвращает стартовую точку связи
        /// </summary>
        /// <returns></returns>
        public SituationBox GetStartLinkPoint()
        {
            foreach (SituationBox sitbox in this)
            {
                if (sitbox.IsLinkStart)
                    return sitbox;
            }
            return null;
        }

        /// <summary>
        /// Сериализация класса
        /// </summary>
        /// <param name="fileName"></param>
        public void SerializeMe(string fileName)
        {            
            /*XmlWriter writer = new XmlTextWriter(fname, System.Text.Encoding.UTF8);
            XmlSerializer serializer = new XmlSerializer(typeof(SituationsArea));
            serializer.Serialize(writer, this);
            writer.Close();*/
            using (FileStream fs = new FileStream(fileName, FileMode.Create))
            {
                BinaryFormatter f = new BinaryFormatter();
                Guid stateId = ProjectDescriptor.SetStateOfPreparationOn(PreparationStates.Serialization);
                try
                {
                    f.Serialize(fs, this);
                }
                finally
                {
                    fs.Close();
                    ProjectDescriptor.SetStateOfPreparationOff(stateId);
                }
            }
        }

        /// <summary>
        /// Десериализация класса
        /// </summary>
        /// <param name="fileName"></param>
        public SituationsArea DeserializeMe(string fileName, int minPercent, int maxPercent)
        {
            /*Processing.SetPercentOfPartByBorders(0, minpercent, maxpercent);
            SituationsArea newSA = null;            
            using (XmlReader reader = new XmlTextReader(fname))
            {
                try
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(SituationsArea));
                    Processing.SetPercentOfPartByBorders(5, minpercent, maxpercent);
                    try
                    {
                        newSA = (SituationsArea)serializer.Deserialize(reader);
                    }
                    catch (Exception ex)
                    {
#if DEBUG
                        throw new BIProjectCorruptedException(ex.Message, ex);
#else
                throw new BIProjectCorruptedException("", ex);
#endif
                    }
                }
                catch (BIProjectCorruptedException ex)
                {
#if DEBUG
                    GeneralMethods.ShowUserError(ex.Message + char.ConvertFromUtf32(10) + char.ConvertFromUtf32(13) + ex.StackTrace);
#else
                GeneralMethods.ShowUserError(ex.Message);
#endif
                    newSA = null;
                }
                reader.Close();
            }
            if (newSA != null)
            {
                Processing.SetPercentOfPartByBorders(60, minpercent, maxpercent);
                newSA.linklines = new Hashtable();
                newSA.Parent = parent;
                _mainformLink.status = Resources.RS_DSRESTORING;
                Processing.SetPercentOfPartByBorders(61, minpercent, maxpercent);
                int perlength = maxpercent - minpercent;
                RestoreBackLinks(newSA, Convert.ToInt32(0.61 * perlength) + minpercent, Convert.ToInt32(0.90 * perlength) + minpercent);
                Processing.SetPercentOfPartByBorders(90, minpercent, maxpercent);
                RestoreSituationBoxes(newSA, Convert.ToInt32(0.90 * perlength) + minpercent, Convert.ToInt32(1.00 * perlength) + minpercent);
            }
            Processing.SetPercentOfPartByBorders(100, minpercent, maxpercent);
            return newSA;*/


            Processing.SetPercentOfPartByBorders(0, minPercent, maxPercent);
            SituationsArea newSA = null;            
            FileStream fs = null;
            try
            {
                try
                {
                    fs = File.OpenRead(fileName);
                }
                catch (System.IO.IOException ex)
                {
#if DEBUG
                    throw new BIProjectCorruptedException(ex, ex.Message);
#else
                    throw new BIProjectCorruptedException(ex, string.Empty);
#endif
                }                
                if (fs == null)
                {
                    throw new BIProjectCorruptedException(null, string.Empty);
                }                
                Processing.SetPercentOfPartByBorders(5, minPercent, maxPercent);
                BinaryFormatter f = new BinaryFormatter();
                try
                {
                    newSA = (SituationsArea)f.Deserialize(fs);
                    if (newSA != null)
                    {
                        Processing.SetPercentOfPartByBorders(60, minPercent, maxPercent);
                        newSA.LinkLines = new Hierarchy<LinkLine>();
                        newSA.Parent = Parent;                        
                    }                    
                }
                catch (SerializationException ex)
                {
#if DEBUG
                    throw new BIProjectCorruptedException(ex, ex.Message);
#else
                    throw new BIProjectCorruptedException(ex, string.Empty);
#endif

                }
                finally
                {
                    if (fs != null)
                    {
                        fs.Close();
                    }
                }
                _projectExplorerForm.Status = Resources.BI_SITUATIONSHIERARCHY_RESTORING;
                Processing.SetPercentOfPartByBorders(61, minPercent, maxPercent);
                int perlength = maxPercent - minPercent;                    
                try
                {
                    RestoreBackLinks(newSA, Convert.ToInt32(0.61 * perlength) + minPercent, Convert.ToInt32(0.90 * perlength) + minPercent);
                    Processing.SetPercentOfPartByBorders(90, minPercent, maxPercent);
                    RestoreSituationBoxes(newSA, Convert.ToInt32(0.90 * perlength) + minPercent, Convert.ToInt32(1.00 * perlength) + minPercent);
                }
                catch (Exception ex)
                {
#if DEBUG
                    throw new BIProjectCorruptedException(ex.InnerException, ex.Message);
#else
                    throw new BIProjectCorruptedException(ex.InnerException, string.Empty);
#endif
                }
            }
            catch (BIProjectCorruptedException ex)
            {
#if DEBUG
                GeneralMethods.ShowUserError(ex.Message + char.ConvertFromUtf32(10) + char.ConvertFromUtf32(13) + ex.StackTrace);
#else
                GeneralMethods.ShowUserError(ex.Message);
#endif
                newSA = null;
            }
            Processing.SetPercentOfPartByBorders(100, minPercent, maxPercent);
            return newSA;
        }

        /// <summary>
        /// Возвращает линию связи по связи
        /// </summary>
        /// <param name="link"></param>
        /// <returns></returns>
        public LinkLine GetLinkLineByLink(Link link)
        {
            foreach(LinkLine ll in _linkLines)
            {                
                Link havelink = _projectLink.GetLinkById(ll.RelatedLinkId);
                if ((havelink != null) && (havelink.SourceCharacteristicId == link.SourceCharacteristicId))
                {
                    return ll;
                }
            }
            return null;            
        }        

        /// <summary>
        /// Добавляет линию связи в список линий связи
        /// </summary>
        /// <param name="linkLine"></param>        
        public void AddLinkLine(LinkLine linkLine)
        {
            _linkLines.Add(linkLine);           
        }

        /// <summary>
        /// Удаляет указанную линию связи из списка линий связи
        /// </summary>                
        public void DeleteLinkLine(LinkLine linkLine)
        {
            if (SelectedLinkLine == linkLine)
                SelectedLinkLine = null;            
            _linkLines.Delete(linkLine);
        }

        /// <summary>
        /// Уменьшает масштаб
        /// </summary>
        public bool DoScaleLess()
        {         
            switch (Scale)
            {
                case SituationsHierarchyScale.VeryLittle:
                    Scale = SituationsHierarchyScale.Panorama;
                    return true;
                case SituationsHierarchyScale.Little:
                    Scale = SituationsHierarchyScale.VeryLittle;
                    return true;
                case SituationsHierarchyScale.Less:
                    Scale = SituationsHierarchyScale.Little;
                    return true;
                case SituationsHierarchyScale.Normal:
                    Scale = SituationsHierarchyScale.Less;
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Увеличивает масштаб
        /// </summary>
        public bool DoScaleLarger()
        {            
            switch (Scale)
            {
                case SituationsHierarchyScale.Panorama:
                    Scale = SituationsHierarchyScale.VeryLittle;
                    return true;
                case SituationsHierarchyScale.VeryLittle:
                    Scale = SituationsHierarchyScale.Little;
                    return true;
                case SituationsHierarchyScale.Little:
                    Scale = SituationsHierarchyScale.Less;
                    return true;
                case SituationsHierarchyScale.Less:
                    Scale = SituationsHierarchyScale.Normal;
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Актуализация цветовой схемы оформления рабочего пространства иерархии ситуаций
        /// </summary>
        public void ActualizeScheme(bool toUpdateLines, SituationBox situationBoxForSelecting)
        {
            IHierarchy<SituationBox> hierarchy = new Hierarchy<SituationBox>();
            if (situationBoxForSelecting != null)
            {
                hierarchy.Add(situationBoxForSelecting);
            }
            ActualizeScheme(hierarchy, toUpdateLines);
        }

        /// <summary>
        /// Актуализация цветовой схемы оформления рабочего пространства иерархии ситуаций
        /// </summary>
        public void ActualizeScheme(IHierarchy<SituationBox> situationBoxesForSelecting, bool toUpdateLines)
        {
            foreach(SituationBox sitbox in this)
            {                
                if ((situationBoxesForSelecting == null) || 
                    (situationBoxesForSelecting.Contains(sitbox) != sitbox.IsFlashedOnFocus))
                {
                    sitbox.UpdateView();
                }
            }
            if (toUpdateLines)
                _situationsHierarchyForm.RefreshSituationsArea();            
        }

        /// <summary>
        /// Возвращает прямоугольник ситуации, относящийся к указанной ситуации
        /// </summary>
        /// <param name="situation"></param>
        /// <returns></returns>
        public SituationBox GetSituationBoxBySituation(Situation situation)
        {
            foreach(SituationBox sitbox in this)            
            {
                if (sitbox.SituationId == situation.Id)
                    return sitbox;
            }
            return null;
        }

        /// <summary>
        /// Возвращает прямоугольник ситуации, относящийся к указанному текстбоксу
        /// </summary>
        /// <param name="situation"></param>
        /// <returns></returns>
        public SituationBox GetSituationBoxByTextBox(TextBox textBox)
        {
            foreach (SituationBox sitbox in this)
            {
                if (sitbox.Control == textBox)
                    return sitbox;
            }
            return null;
        }

        /// <summary>
        /// Инициализация нового рабочего пространства (сброс настроек)
        /// </summary>
        public void ResetSituationsArea()
        {            
            while (ItemsCount > 0)
            {
                foreach (SituationBox sitbox in this)
                {
                    sitbox.Kill(false);
                    break;
                }
            }
            _situationsHierarchyForm.UpdateCharacteristicsGrid(null);
            Clear();
            _linkLines = new Hierarchy<LinkLine>();
            _situationsHierarchyForm.RefreshSituationsArea();
            _situationsHierarchyForm.EditingRulesContainerId = Guid.Empty;
        }        
        #endregion                                                                                

        #region конструкторы и финализаторы
        /// <summary>
        /// Конструктор класса
        /// </summary>
        public SituationsArea(ProjectExplorerForm projectExplorerLink, SituationsHierarchyForm situationsHierarchyForm)
        {
            _projectLink = projectExplorerLink.ProjectLink;
            _projectExplorerForm = projectExplorerLink;            
            _situationsHierarchyForm = situationsHierarchyForm;                                        
            InitializeClass();
        }
        #endregion
    }
}
